package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"time"

	"github.com/tjfoc/gmsm/sm2"
)

var (
	ecdsaCertPath = "ecdsa.cert.pem"
	sm2CertPath   = "sm2.cert.pem"
)

func main() {
	parseECDSAcert()
	// parseSM2Cert()
	ecdsaTosm2()
	// generateSM2Cert()
}

func ecdsaTosm2() {
	// ecdsa.GenerateKey(, rand)
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		fmt.Println(err)
		return
	}

	b, err := x509.MarshalECPrivateKey(key)
	if err != nil {
		fmt.Println(err)
		return
	}
	block := &pem.Block{
		Type:  "BEGIN PRIVATE KEY",
		Bytes: b,
	}
	out := pem.EncodeToMemory(block)
	fmt.Println(string(out))
	// fmt.Println(string(b))
	key, err = x509.ParseECPrivateKey(b)
	if err != nil {
		fmt.Println(err)
		return
	}

	key2, err := sm2.ParseSm2PrivateKey(b)
	if err != nil {
		fmt.Println(err)
		return
	}

	template := SM2Template()
	template.IsCA = true
	template.KeyUsage |= sm2.KeyUsageDigitalSignature |
		sm2.KeyUsageKeyEncipherment | sm2.KeyUsageCertSign |
		sm2.KeyUsageCRLSign
	template.ExtKeyUsage = []sm2.ExtKeyUsage{
		sm2.ExtKeyUsageClientAuth,
		sm2.ExtKeyUsageServerAuth,
	}
	template.Subject.CommonName = "yangyf"
	template.Subject.Country = []string{"CN"}
	template.Subject.Province = []string{"FuJian"}

	t2 := x509Template()
	t2.IsCA = true
	t2.KeyUsage |= x509.KeyUsageDigitalSignature |
		x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign |
		x509.KeyUsageCRLSign
	t2.ExtKeyUsage = []x509.ExtKeyUsage{
		x509.ExtKeyUsageClientAuth,
		x509.ExtKeyUsageServerAuth,
	}
	t2.Subject.CommonName = "ecdsa"
	buf, _ := x509.CreateCertificate(rand.Reader, t2, t2, &key.PublicKey, key)
	block = &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: buf,
	}
	out2 := pem.EncodeToMemory(block)
	fmt.Println(string(out2))
	ioutil.WriteFile("out2.pem", out2, 0644)

	buf, _ = sm2.CreateCertificate(rand.Reader, template, template, &key2.PublicKey, key)
	block = &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: buf,
	}
	out2 = pem.EncodeToMemory(block)
	fmt.Println(string(out2))
	ioutil.WriteFile("out3.pem", out2, 0644)

	xx, err := sm2.CreateCertificateToPem("ecdsatosm.pem", template, template, &key2.PublicKey, key2)
	fmt.Println(xx, err)

}

func generateSM2Cert() {
	key, err := sm2.GenerateKey()
	if err != nil {
		fmt.Println(err)
		return
	}

	template := SM2Template()
	template.IsCA = true
	template.KeyUsage |= sm2.KeyUsageDigitalSignature |
		sm2.KeyUsageKeyEncipherment | sm2.KeyUsageCertSign |
		sm2.KeyUsageCRLSign
	template.ExtKeyUsage = []sm2.ExtKeyUsage{
		sm2.ExtKeyUsageClientAuth,
		sm2.ExtKeyUsageServerAuth,
	}
	template.Subject.CommonName = "yangyf"
	template.Subject.Country = []string{"CN"}
	template.Subject.Province = []string{"FuJian"}

	sm2.CreateCertificateToPem(sm2CertPath, template, template, &key.PublicKey, key)

}

func parseSM2Cert() {
	buf, err := ioutil.ReadFile(ecdsaCertPath)
	if err != nil {
		fmt.Println(err)
		return
	}
	block, _ := pem.Decode(buf)
	if block == nil {
		fmt.Println("get empty block")
		return
	}
	cer, err := sm2.ParseCertificate(block.Bytes)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(cer.Subject)

}

func parseECDSAcert() {
	buf, err := ioutil.ReadFile("out3.pem")
	if err != nil {
		fmt.Println(err)
		return
	}
	block, _ := pem.Decode(buf)
	if block == nil {
		fmt.Println("get empty block")
		return
	}
	cer, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(cer.Subject)
}

func x509Template() *x509.Certificate {
	// generate a serial number
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)

	// set expiry to around 10 years
	expiry := 3650 * 24 * time.Hour
	// round minute and backdate 5 minutes
	notBefore := time.Now().Round(time.Minute).Add(-5 * time.Minute).UTC()

	//basic template to use
	x509 := &x509.Certificate{
		SerialNumber:          serialNumber,
		NotBefore:             notBefore,
		NotAfter:              notBefore.Add(expiry).UTC(),
		BasicConstraintsValid: true,
	}
	return x509
}

func SM2Template() *sm2.Certificate {
	// generate a serial number
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)

	// set expiry to around 10 years
	expiry := 3650 * 24 * time.Hour
	// round minute and backdate 5 minutes
	notBefore := time.Now().Round(time.Minute).Add(-5 * time.Minute).UTC()

	//basic template to use
	sm2 := &sm2.Certificate{
		SerialNumber:          serialNumber,
		NotBefore:             notBefore,
		NotAfter:              notBefore.Add(expiry).UTC(),
		BasicConstraintsValid: true,
	}
	return sm2
}
